classdef Pseudosubs
    properties (Constant)
        PSEUDOSUBS_OPEN = '$['
        PSEUDOSUBS_CLOSE = ']$'        
    end
    
    
    
    
    methods (Static)
        function code = parse(varargin)
            import preparser.Pseudosubs;
            import preparser.Preparser;
            if nargin==1
                % (Preparser):
                p = varargin{1};
                code = p.Code;
                assigned = p.Assigned;
            else % (code,assigned):
                code = varargin{1};
                assigned = varargin{2};
            end
            sh = Pseudosubs.createShadowCode(code);
            level = cumsum(sh);
            if any(level>1)
                posNested = find(level>1,1);
                throwCode( exception.Preparser.PSEUDOSUBS_NESTED, ...
                    code(posNested:end) );
            end
            lenOpen = length(Pseudosubs.PSEUDOSUBS_OPEN);
            lenClose = length(Pseudosubs.PSEUDOSUBS_CLOSE);
            while true
                posOpen = find(level==1,1);
                if isempty(posOpen)
                    break
                end
                posClose = posOpen + find(level(posOpen+1:end)==0,1);
                if isempty(posClose)
                    throwCode( exception.Preparser.PSEUDOSUBS_NOT_CLOSED, ...
                        code(posOpen:end) );
                end
                expn = code(posOpen+lenOpen:posClose-1);
                try
                    value = Preparser.eval(expn,assigned);
                    s = Pseudosubs.printValue(value);
                catch
                    throwCode( exception.Preparser.PSEUDOSUBS_EVAL_FAILED, ...
                        code(posOpen:posClose+lenClose-1) );
                end
                code = [ ...
                    code(1:posOpen-1), ...
                    s, ...
                    code(posClose+lenClose:end), ...
                    ];
                level = [ ...
                    level(1:posOpen-1), ...
                    zeros(1,length(s),'int8'), ...
                    level(posClose+lenClose:end), ...
                    ];
            end
            p.Code = code;
        end
        
        
        
        
        function sh = createShadowCode(c)
            import preparser.Pseudosubs;
            sh = zeros(1,length(c),'int8');
            strOpen = Pseudosubs.PSEUDOSUBS_OPEN;
            strClose = Pseudosubs.PSEUDOSUBS_CLOSE;
            posOpen = strfind(c,strOpen);
            if ~isempty(posOpen)
                sh(posOpen) = 1;
            end
            posClose = strfind(c,strClose);
            if ~isempty(posClose)
                sh(posClose) = -1;
            end
        end
        
        
        
        
        function c = printValue(value)
            if isnumeric(value) || islogical(value) || ischar(value)
                value = num2cell(value);
            elseif ~iscell(value)
                value = { value };
            end
            c = '';
            for i = 1 : length(value)
                if ischar(value{i})
                    c = [c,value{i},',']; %#ok<AGROW>
                elseif isnumeric(value{i})
                    c = [c,sprintf('%g,',value{i})]; %#ok<AGROW>
                elseif isequal(value{i},true)
                    c = [c,'true,']; %#ok<AGROW>
                elseif isequal(value{i},false)
                    c = [c,'false,']; %#ok<AGROW>
                else
                    c = char(value{i});
                    c = c(:).';
                end
            end
            c = c(1:end-1);
        end
    end
end
